44. Separate parameter dependent code from template

템플릿을 사용하면, 코드 비대화(code bloat)을 초래할 수 있다.
아래에서는 비타입 템플릿 매개변수로 인한 코드 비대화만 다룸

공통성 및 가변성 분석(commonality and variability)
클래스에서 공통적인 부분을 양쪽에 두지 않음
(공통 부분을 새로운 클래스에 옮기 후, 클래스 상속 혹은 객체 합성을 사용해 원래의 클래스가 공통 부분을 공유)

템플릿이 아닌 코드는 코드 중복이 명시적인 반면,
템플릿인 코드는 코드 중복이 암시적이다.
(소스코드상에서는 템플릿이 하나만 존재하며, 인스턴스화 할 때, 코드가 생성된다.)
template <typename T, std::size_t n>
class SquareMatrix{ //
public:
// ...
void inver(void); //
};
위 템플릿은 T라는 타입 매개변수와, size_t 타입의 비타입 매개변수(non-type parameter)인 n을 인자로 한다.
SquareMatrix<double, 5> sm1;
// ...
sm1.invert(); // SquareMatrix<double, 5>::invert
SquareMatrix<double, 10> sm2;
// ...
sm2.invert(); // SquareMatrix<double, 10>::invert
위에서 SquareMatrix<double, 5>와 SquareMatrix<double, 10> 클래스 코드가 따로 생성된다.
(내부의 공통으로 사용되는 함수도 함께 사본으로 인스턴스화 되어, 코드 비대화를 일으킴)
template <typename T>
class SquareMatrixBase{
protected:
// ...
void invert(std::size_t matrixsize);
// ...
};
template <typename T, std::size_t n>
class SquareMatrix: private SquareMatrixBase<T>{
private:
using SquareMatrixBase<T>::invert;
public:
// ...
void invert(void){ this->invert(n); }
};
invert 함수는 매개변수 타입 T에 대해서만 템플릿화 되어 있다.
동등한 타입 T에 대해서 SquareMatrix는 항상 같은 SquareMatrixBase<T>::invert 함수를 공유

SquareMatrixBase::invert 함수에서 역함수를 계산하기 위해서는 실제 데이터에 접근할 수 있어야 한다.

정적 메모리 할당
template <typename T>
class SquareMatrixBase{
protected:
SquareMatrixBase(std::size_t n, T* pMem): size(n), pData(pMem){}
void setDataPtr(T* ptr){ pData=ptr; }
// ...
private:
std::size_t size;
T* pData;
};
template <typename T, std::size_t n>
class SquareMatrix: private SquareMatrixBase<T>{
public:
SquareMatrix(): SquareMatrixBase<T>(n, data) {}
// ...
private:
T data[n*n]; //
}
동적 메모리 할당
template <typename T, std::size_t n>
class SquareMatrix: private SquareMatrixBase{
public:
SquareMatrix(): SquareMatrixBase<T>(n, nullptr), pData(new T[n*n]){
this->setDataPtr(pData.get());
}
// ...
private:
boost::scoped_array<T> pData;
};
이와 같이 동일하게 수행되는 함수에 대해서 서로 코드를 공유하도록 코딩할 수 있다.

컴파일러에게 더 높은 수준의 최적화를 유도하기 위해서는,
크기 독립형 버전을 사용하지 않는것이 좋기는 하다.

하지만, 동일한 함수에 대해 공통된 코드를 공유하도록 코딩할 경우,
프로세스를 실행할 때, 프로그램의 작업 세트의 크기가 줄어들어 명령어 캐시 내의 참조 지역성도 향상될 수 있다.
타입 매개변수로 인한 코드 비대화
템플릿 매개변수화에서 vector<int>와 vector<long>의 멤버 함수는 거의 동일하다.
(int와 long은 이진 표현구조가 동일함)
컴파일러 링커에 따라서 이와 같은 동일한 표현구조를 함수를 합쳐주는 기능을 제공하기도 한다.

대부분의 컴파일러는 포인터 타입의 경우, void*로 동작하는 버전을 호출하여 코드 비대화를 막는다.
list<int*>, list<const int*>, list<SquareMatrix<long, 3>*> 등은 이진 수준에서 멤버 함수 집합을
공유해도 무방하다.
하지만 위의 STL은 멤버 함수를 구현할 때, 타입 미정 포인터(void*)로 동작하는 버전을 호출해서 만든다.
비타입 템플릿 매개변수는 템플릿 매개변수를 함수 매개변수 혹은 클래스 데이터 멤버로 대체하여 비대화를 막음
타입 템플릿 매개변수는 동일한 이진 표현 구조를 가지고 인스턴스화 하는 경우, 한 가지 함수를 공유하도록 구현하여
코드 비대화를 막음